// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Infrastructure;

namespace SnapObjects.Data.AspNetCore
{
    /// <summary>
    ///     Defines a contract that represents the result of an action method which can be used to roll back 
    ///     the current transaction. only works when TransactionAttribute is applied to the action(or the controller)
    ///     to start an automatically managed transaction.
    /// </summary>
    public interface IRollbackResult : IActionResult
    {
    }

    /// <summary>
    ///     Provides a way to return an action result and rolls back the current transaction. It only works when 
    ///     TransactionAttribute is applied to the action(or the controller) to start an automatically managed transaction.
    /// </summary>
    /// <typeparam name="TValue"></typeparam>
    public class RollbackResult<TValue> : ActionResult, IRollbackResult
    {
        private readonly ActionResult<TValue> _result;

        /// <summary>
        ///     Initializes a new instance of the <see cref="RollbackResult{TValue}"/> class.
        /// </summary>
        /// <param name="result">
        ///     An <see cref="ActionResult{TValue}"/> object that wraps either a TValue instance or an ActionResult.
        /// </param>
        public RollbackResult(ActionResult<TValue> result)
        {
            _result = result;
        }

        /// <summary>
        ///     Executes the result operation of the action method synchronously. This method is called 
        ///     by MVC to process the result of an action method.
        /// </summary>
        /// <param name="context">
        ///     The context in which the result is executed. The context information includes information 
        ///     about the action that was executed and request information.
        /// </param>
        public override void ExecuteResult(ActionContext context)
        {
            this.ExecuteResultAsync(context).Wait();
        }

        /// <summary>
        ///     Executes the result operation of the action method asynchronously. This method is called 
        ///     by MVC to process the result of an action method.
        /// </summary>
        /// <param name="context">
        ///     The context in which the result is executed. The context information includes information 
        ///     about the action that was executed and request information.
        /// </param>
        /// <returns></returns>
        public override Task ExecuteResultAsync(ActionContext context)
        {
            if (_result is IConvertToActionResult convert)
            {
                return convert.Convert().ExecuteResultAsync(context);
            }

            return Task.CompletedTask;
        }

        /// <summary>
        ///     Defines an implicit conversion of an <see cref="ActionResult{TValue}"/> to a <see cref="RollbackResult{TValue}"/>.
        /// </summary>
        /// <param name="result">
        ///     The <see cref="ActionResult{TValue}"/> to convert to a <see cref="RollbackResult{TValue}"/>.
        /// </param>
        public static implicit operator RollbackResult<TValue>(ActionResult<TValue> result)
        {
            return new RollbackResult<TValue>(result);
        }

        /// <summary>
        ///     Defines an implicit conversion of a TValue to a <see cref="RollbackResult{TValue}"/>.
        /// </summary>
        /// <param name="value">
        ///     The value to convert to a <see cref="RollbackResult{TValue}"/>.
        /// </param>
        public static implicit operator RollbackResult<TValue>(TValue value)
        {
            return new RollbackResult<TValue>(value);
        }
    }
}
